/**
 *  \file I3Id.cpp
 *  \brief Defines an i3 identifier.
 */

#include "i3_debug.h"
#include <string>
#include <string.h>
#include <assert.h>

#include <ctype.h>

#include "OCD_include.h"

#include "I3Id.h"
#include "i3CommonHeaders.h"

extern "C" {
    #include "i3_id.h"
};

extern "C" ID* duplicate_i3_id(ID *id); //TODO remove this

/**
  * Constructor.
  */
I3Id::I3Id() {
    
    //blank constructor
}

/**
  * Creates a new I3Id with the specified value.
  * @param valId The id to be copied into the I3Id being created.
  */
I3Id::I3Id(ID *valId) {
    memcpy (id.x, valId->x, sizeof(ID));    
}

/**
  * Constructs a new I3Id from string representation of the id.
  * This different from generating an I3 id from an arbitrary string.
  * @param idStr The string representation of the I3 id.
  * @pararm idType I3_ID_TYPE_PRIVATE or I3_ID_TYPE_PUBLIC
  */
I3Id::I3Id(string idStr, char idType) {

    this->idStr = idStr;
    char* tmpStr = strdup(idStr.c_str());
    int i, len;
    
    assert((len = strlen(tmpStr)) <= 2* I3_ID_LEN);
    memset(id.x, 0, I3_ID_LEN);
    
    if (len % 2 != 0) {
        tmpStr[len] = '0';
        len++;
    }
    
    for (i = 0; i < len/2; i++)
        id.x[ i ] = (toDigit(tmpStr[2*i]) << 4) | toDigit(tmpStr[2*i+1]);

    tmpStr[len--] = 0;  // to restore old tmpStr
    delete[] tmpStr;

    setIdType(idType);
}

/**
  * Returns an I3 Id generated from the specificed string.
  * This is different from the I3Id constructor which converts an
  * existing string representation of the id (stored in a file etc)
  * to an I3Id in memory.
  * @param str The string from which the id is to be generated.
  * @pararm idType I3_ID_TYPE_PRIVATE or I3_ID_TYPE_PUBLIC
  * @return A pointer to a new I3Id
  */
I3Id* I3Id::generateI3Id(string str, char idType) {

    I3Id *retId = new I3Id();
    
    const char* tmpStr = str.c_str();
    int len = strlen(tmpStr);
    
    int i;
    for (i = 0; i < I3_ID_LEN; i++) {
        retId->id.x[i] = tmpStr[i % len];
    }
    retId->setIdType(idType);
    return retId;
}

/**
  * Gives a string representation (pretty printed) of the I3Id
  * @param tmpStr a character buffer in which the string is stored.
  * Sufficient space should be allocated for tmpStr.
  * This function is used for displaying the id on screen.
  * @return pointer to tmpStr
  */
char* I3Id::toString(char *tmpStr) {
    
    sprintf_i3_id(tmpStr, &this->id);
    return tmpStr;
}

/**
  * Convert the specified id to a string.  THis function is used to generate
  * the key for hash tables keyed on the ids.
  * @param id The id to be converted.
  * @return a string representation of the id. 
  */
string I3Id::convertIdToStr(ID id) {
    char tmpIdBuf[MAX_PRINT_BUF];
    tmpIdBuf[0] = '\0';
    
    for (int index = 0; index < I3_ID_LEN; index++) {
        char tmpNumBuf[MAX_PRINT_BUF];
        sprintf(tmpNumBuf, "%u", id.x[index]);
        strcat(tmpIdBuf, tmpNumBuf);
    }
    
    return string (tmpIdBuf);
}

unsigned char I3Id::toDigit(char ch) {
    if (isdigit((int) ch))
    return (ch - '0');
    else
    return (10 + ch - 'a');
}


/**
  * Set the ID type of this id.
  * @param idType I3_ID_TYPE_PRIVATE, I3_ID_TYPE_PUBLIC
  */
void I3Id::setIdType(char idType) {
    set_id_type (&id, idType);
}

/**
  * Checks if this I3 id is of type public or not.
  * return true if public, else false.
  */
bool I3Id::isPublic() {

    if (I3_ID_TYPE_PUBLIC == get_id_type (&id)) {
        return true;
    } else {
        return false;
    }
}
/**
  * Checks if this I3 id is of type private or not.
  * return true if private, else false.
  */
bool I3Id::isPrivate() {

    if (I3_ID_TYPE_PRIVATE == get_id_type (&id)) {
        return true;
    } else {
        return false;
    }
}

/**
  * Returns a pointer to a duplicate of the ID. 
  */
ID* I3Id::getID() {
    return duplicate_i3_id(&id);
}

/** This function fills the the specified id with 
  * an id generated by hashing the specified URL curl.
  * The id is automatically set to be of type I3_ID_TYPE_PRIVATE.
  * IDs generated by hashing URLs are of type private and right constrained.
  * The naming is confusing because usually 'public' ids are generated by
  * hashing names and we are setting the type of these ids as private.
  */
void I3Id::hash_URL_on_ID(const char* curl, ID *id) {
    
	memset(id, 0, ID_LEN);

	char tmp[MAX_URL_LEN];
	if (strlen (curl) > MAX_URL_LEN - 1) {
		I3_PRINT_DEBUG1 (	
					DEBUG_LEVEL_MINIMAL,
					"I3Id:has_URL_on_ID: Very long url %s.\n",
					curl
				);
		return;	
	}
	strcpy (tmp, curl);
    strlwr(tmp);
    memset(id, 0, ID_LEN);
    strncpy((char *)id->x, tmp, ID_LEN);
    
    set_id_type (id, I3_ID_TYPE_PRIVATE); 

}


/** Generates a random private i3 id and places it
  * in id.
  * @param id The generate private id is stored here.
  */
void I3Id::gen_private_ID(ID *id) {

    get_random_ID(id);
    set_private_id(id);

    //TODO
    //if ( use_ping )
    //  update_prefix();

    //TODO Implement command line prefix len
    // Then enable the following if block.
    /*
    if ( actual_prefix_len ) {
    
        for (i = 0; i < DEFAULT_PREFIX_LEN; i++) {
            id->x[i] = default_prefix.x[i];
        }

        for(i = (DEFAULT_PREFIX_LEN-1); i>=0;i--) {
            if ( id->x[i] != 0) {
                
                id->x[i]--;
                break;
            
            } else {
                
                id->x[i] = 0xff;
            }
        }
    }
    */
}

/**
  * Generate a random id and put in id.
  * @param id The random id generated is stored here.
  */
void I3Id::get_random_ID(ID *id) {
  
    int   i;
   
    for(i=0; i < ID_LEN; i++) {
#ifdef _WIN32
        id->x[i] = (char) (rand() % 255);
#else 
        id->x[i] = (char) (rand() % 255);

#endif
    }
}

/**
  * Converts a string representation of the id into
  * an actual ID.
  * This is the function used in the address book.
  */
void I3Id::convertStr2I3Id (const char *nid, ID *cnid) {
    
    for(int i=0;i<ID_LEN;i++) {
        char str[3];

        if ( (i * 2) < (int) strlen(nid)) {
            str[0] = nid[i*2];
        } else {
            str[0] = '0';
        }
            
        if ( (i * 2 + 1) < (int) strlen(nid)) {
            str[1] = nid[i*2 + 1];
        }else {
            str[1] = '0';
        }
      
        str[2] = 0;
        cnid->x[i] = (unsigned char) strtol(str, NULL, 16);
    }
}
